﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EPiServer.Core;
using EPiServer.UI.Edit.PageCompare.Core;
using EPiServer.HtmlParsing;
using EPiServer.Web;
using EPiServer.Labs.MobilePack.Core.modules.MobilePack.Core.Manifest;
using EPiServer.Data.Dynamic;
using System.IO;
using System.Web;

namespace EPiServer.Labs.MobilePack.Core.modules.MobilePack.Core
{
    public class ManifestFactory
    {
        #region Singleton

        private static ManifestFactory instance;
      
        public static ManifestFactory Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new ManifestFactory();            
                }

                return instance;
            }
        }

        #endregion

        public Dictionary<string, string> LocalURLs { get; set; }
        public DynamicDataStore store
        {
            get
            {
               return  DynamicDataStoreFactory.Instance.GetStore(typeof(ManifestModel)) ?? DynamicDataStoreFactory.Instance.CreateStore(typeof(ManifestModel));  
            }
       
        }

        public ManifestModel Settings { get; set; }

        public ManifestFactory()
        {
             LocalURLs = new Dictionary<string, string>();
                 
        }

        public void AddImagesForJQueryMobile()
        {
            UpdateFile("/modules/MobilePack.Core/images/icons-18-black.png", "/modules/MobilePack.Core/images/icons-18-black.png");
            UpdateFile("/modules/MobilePack.Core/images/icons-18-white.png", "/modules/MobilePack.Core/images/icons-18-white.png");
            UpdateFile("/modules/MobilePack.Core/images/icons-36-black.png", "/modules/MobilePack.Core/images/icons-36-black.png");
            UpdateFile("/modules/MobilePack.Core/images/icons-36-white.png", "/modules/MobilePack.Core/images/icons-36-white.png");
            UpdateFile("/modules/MobilePack.Core/images/ajax-loader.png", "/modules/MobilePack.Core/images/ajax-loader.png");
        }

        public ManifestModel LoadModel()
        {         
            return store.Items<ManifestModel>().FirstOrDefault();
        }

        public void Save(ManifestModel model)
        {         
            store.Save(model);
        }

        /// <summary>
        /// Find all pages under the page set in the settings area in admin mode
        /// Get all the links from that page and include them
        /// </summary>
        public void FindPagesAndAddThem()
        {
            var model = LoadModel();
            if (model != null)
            {
                if (!PageReference.IsNullOrEmpty(model.StartPoint))
                {
                    try
                    {
                        var AllChildren = DataFactory.Instance.GetDescendents(model.StartPoint);
                        if (model.IncludeStartPage)
                        {
                            AllChildren.Add(model.StartPoint);
                        }
                        foreach (var pr in AllChildren)
                        {
                            var pd = DataFactory.Instance.GetPage(pr);
                            UpdateFile(pd.PageGuid.ToString(), GetFriendlyUrl(pd));
                            UpdateLinksOnPage(pd);
                        }

                    }
                    catch (Exception) { }
                }
            }
        }


        /// <summary>
        /// Create a string to be saved in the Manifest file
        /// </summary>
        /// <returns></returns>
        public string MakeManifestFileAsString()
        {
            AddImagesForJQueryMobile();

            StringBuilder strb = new StringBuilder();
            strb.Append("CACHE MANIFEST" + Environment.NewLine);
            strb.AppendFormat("# This file was generated at {0}" + Environment.NewLine, DateTime.Now);
           
            foreach (string filePath in LocalURLs.Values)
            {
                strb.AppendFormat(String.Format("{0}" + Environment.NewLine, filePath));
            }

            strb.Append("NETWORK:"+Environment.NewLine);
            strb.Append("http://*" + Environment.NewLine);
            strb.Append("https://*" + Environment.NewLine);
            strb.Append("*" + Environment.NewLine);

            return strb.ToString();
        }

        /// <summary>
        /// Write the Manifest file in the root fot he site
        /// </summary>
        public void WriteManifestFile()
        {
            try
            {
                TextWriter writer = new StreamWriter(HttpContext.Current.Request.MapPath("~/MobilePack.manifest"));
                writer.WriteLine(MakeManifestFileAsString());
                writer.Close();
            }
            catch { };
        }

        //Update a page if an event is changed
        public void UpdatePage(PageData pd)
        {
            UpdateFile(pd.PageGuid.ToString(), GetFriendlyUrl(pd));
            if (pd != null)
            {
                UpdateLinksOnPage(pd);
            }
        }

        public void UpdateFile(string key, string file)
        {
            //never add the manifest file to the offline cache
            if (!file.ToLower().Contains(".manifest"))
            {
                if (LocalURLs.Keys.Contains(key))
                {
                    LocalURLs[key] = file;
                }
                else
                {
                    LocalURLs.Add(key, file);
                }
            }
        }

        /// <summary>
        /// Get links on a specific page and save them
        /// </summary>
        /// <param name="pd"></param>
        public void UpdateLinksOnPage(PageData pd)
        {
            var html = PageComparerProviderBase.CreateInstance().GetHtmlFromPage(pd);
            var sr = new HtmlStreamReader(html);
            var LinkResult = sr.OfType<ElementFragment>().SelectMany(ee => ee.Attributes).
                Where(a => (a.Token == AttributeToken.Src || a.Token == AttributeToken.Href)).Select(a => a.UnquotedValue);

            foreach (var s in LinkResult)
            {
                var url2 = new UrlBuilder(s);
                Global.UrlRewriteProvider.ConvertToInternal(url2);
                var pageref = PermanentLinkUtility.GetPageReference(url2);
                if (pageref != PageReference.EmptyReference)
                {
                    UpdateFile(DataFactory.Instance.GetPage(pageref).PageGuid.ToString(), s);
                }
                else if(!s.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
                {
                    UpdateFile(s, s);
                }
            }
        }

        /// <summary>
        /// Get friendly url from PageData
        /// </summary>
        /// <param name="pd"></param>
        /// <returns></returns>
        public string GetFriendlyUrl(PageData pd)
        {
            UrlBuilder url = new UrlBuilder(pd.LinkURL); 
            EPiServer.Global.UrlRewriteProvider.ConvertToExternal(url, pd.PageLink, System.Text.UTF8Encoding.UTF8);
            return url.ToString();
        }
    }
    
}
